package com.autonavi.yunda.yunji.core.engine.script;

import com.autonavi.yunda.yunji.common.exception.AmapException;
import com.autonavi.yunda.yunji.common.exception.AmapExceptionCode;
import com.autonavi.yunda.yunji.common.utils.groovy.GroovyUtils;
import com.autonavi.yunda.yunji.core.engine.exception.EngineException;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

import static com.autonavi.yunda.yunji.core.config.EngineConstant.EXEC_GET_PROPERTY_PROCESS;
import static com.autonavi.yunda.yunji.core.config.EngineConstant.EXEC_SET_PROPERTY_PROCESS;

/**
 * @author cizhong.wcs
 * @date 2022/6/29 20:12
 */
@Slf4j
public class BaseGroovyScriptClass {
        protected String script;
    protected Class<?> clazz;
    protected Map<String, Method> methodMap;

    public BaseGroovyScriptClass(String script) {
        Class<Object> clazz = GroovyUtils.loadClassGroovyScriptWithCache(script);
        this.methodMap = Arrays.stream(clazz.getDeclaredMethods()).collect(Collectors.toMap(Method::getName, m -> m));
        this.methodMap.putAll(
                Arrays.stream(clazz.getMethods())
                        .filter(method -> EXEC_GET_PROPERTY_PROCESS.equals(method.getName()) || EXEC_SET_PROPERTY_PROCESS.equals(method.getName()))
                        .collect(Collectors.toMap(Method::getName, m -> m))
        );
        this.script = script;
        this.clazz = clazz;
    }

    public Object newInstance() {
        try {
            return clazz.getDeclaredConstructor().newInstance();
        } catch (Exception e) {
            throw new AmapException(AmapExceptionCode.COMMON_SERVER_ERROR, "脚本实例化失败", e);
        }
    }

    public Object invoke(Object obj, String functionName, Object... args) {
        Method method = methodMap.get(functionName);
        try {
            return method.invoke(obj, args);
        } catch (InvocationTargetException e) {
            Throwable targetException = e.getTargetException();
            if (targetException instanceof EngineException) {
                log.warn("调用方法异常 function = {}", functionName, targetException);
            } else {
                log.error("调用方法异常 function = {}", functionName, targetException);
            }
            log.warn("调用方法异常 function = {}", functionName, targetException);
            if (targetException instanceof RuntimeException) {
                throw (RuntimeException) targetException;
            } else {
                throw AmapException.wrap(targetException);
            }
        } catch (Exception e) {
            log.error("调用方法异常 function = {}", functionName, e);
            throw AmapException.wrap(e);
        }
    }

    public void injectParams(Object obj, Map<String, Object> params) {
        if (Objects.isNull(params)) {
            return;
        }
        params.forEach((key, value) -> invoke(obj, EXEC_SET_PROPERTY_PROCESS, key, value));
    }

}
